﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Net;
using System.Net.Sockets;


/*
 * 一个经典的UDP编程，利用了socket，而不是UdpClient
 * 可以保留学习原理
 */

namespace UDPDemo
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        Socket client;
        Semaphore udpSendSem = new Semaphore(0, 10);
        Semaphore udpRecvSem = new Semaphore(0, 1);
        Thread t1;
        Thread t2;
        int sendCount;
        int recvCount;

        public MainWindow() {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e) {
            string A = "";
            string B = "";

            if (t1 != null) {
                if (t1.IsAlive == true) {
                    A = "UDP发送线程：已启动\n";
                } else {
                    A = "UDP发送线程：已结束\n";
                }
            } else {
                A = "UDP发送线程：未创建\n";
            }

            if (t2 != null) {
                if (t2.IsAlive == true) {
                    B = "UDP接收线程：已启动\n";
                } else {
                    B = "UDP接收线程：已结束\n";
                }
            } else {
                B = "UDP接收线程：未创建\n";
            }

            MessageBox.Show("学习demo\n" + "本地端口写0将随机分配\n" + A + B, "关于");
        }

        private void Button_Click_1(object sender, RoutedEventArgs e) {
            Button btn = sender as Button;
            btn.Content = "已连接";
            btn.IsEnabled = false;
            StopButton.IsEnabled = true;
            HexCheckBox.IsEnabled = false;
            InputMsg.IsEnabled = false;
            SendButton.IsEnabled = true;

            // 新建线程，IsBackground置1，子线程会在主程序退出后，也一并退出
            t1 = new Thread(udpSendFunc);
            t1.IsBackground = true;
            t2 = new Thread(udpRecvFunc);
            t2.IsBackground = true;
            t1.Start();
            t2.Start();
        }

        private void Button_Click_2(object sender, RoutedEventArgs e) {
            InitButton.Content = "连接UDP";
            InitButton.IsEnabled = true;
            StopButton.IsEnabled = false;
            HexCheckBox.IsEnabled = true;
            InputMsg.IsEnabled = true;
            SendButton.IsEnabled = false;

            // 退出线程
            client.Shutdown(SocketShutdown.Both);
            client.Close();
            t2.Abort();
            t1.Abort();
        }

        private void InputMsg_TextChanged(object sender, TextChangedEventArgs e) {

        }

        private void HexCheckBox_Checked(object sender, RoutedEventArgs e) {
            InputMsg.Text = "";
        }

        private void Button_Click_3(object sender, RoutedEventArgs e) {  // 发送按钮
            udpSendSem.Release();
        }

        void udpSendFunc() {
            // UDP发送子线程

            IPAddress ipadd = new IPAddress(0);
            int port = 0;
            this.Dispatcher.Invoke(new Action(delegate {
                port = Int32.Parse(LocalPortTextBox.Text);
            }));

            client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            EndPoint localpoint = new IPEndPoint(IPAddress.Any, port);  // 自动使用本机IP，htonl(INADDR_ANY)
            client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);  //设置SOCKET允许多个SOCKET访问同一个本地IP地址和端口号
            client.Bind(localpoint);  // 绑定本机信息

            // 声明一个EndPoint，为发送使用
            this.Dispatcher.Invoke(new Action(delegate {
                ipadd = IPAddress.Parse(TargetIPTextBox.Text);
                port = Int32.Parse(TargetPortTextBox.Text);
            }));
            EndPoint point = new IPEndPoint(ipadd, port);
            udpRecvSem.Release();  // 可以开始接收

            while (true) {
                udpSendSem.WaitOne();
                string msg = string.Empty;
                this.Dispatcher.Invoke(new Action(delegate {
                    if (HexCheckBox.IsChecked == true)  // 字符串转16进制算法
                    {
                        msg = InputMsg.Text;
                        string[] hexValuesSplit = msg.Split(' ');  // 去除空格
                        int count = 0;
                        byte[] byteArray = new byte[hexValuesSplit.Length];
                        foreach (String hex in hexValuesSplit)  // 每个字符串都转成字符
                        {
                            int value = Convert.ToInt32(hex, 16);
                            byte charValue = (byte)value;
                            byteArray[count] = charValue;
                            count++;
                        }
                        client.SendTo(byteArray, point);
                    } else {
                        msg = InputMsg.Text;
                        client.SendTo(Encoding.UTF8.GetBytes(msg), point);
                    }
                }));
                //client.SendTo(Encoding.UTF8.GetBytes(msg), point);

                sendCount++;
                this.Dispatcher.Invoke(new Action(delegate {
                    SendLable.Content = "Send " + Convert.ToString(sendCount);
                }));

            }
        }

        void udpRecvFunc() {
            //IPAddress ipadd = new IPAddress(0);
            //int port = 0;
            //this.Dispatcher.Invoke(new Action(delegate
            //{
            //    ipadd = IPAddress.Parse(TargetIPTextBox.Text);
            //    port = Int32.Parse(TargetPortTextBox.Text);
            //}));
            //EndPoint point = new IPEndPoint(ipadd, port);

            EndPoint point = new IPEndPoint(0, 0);
            byte[] buffer = new byte[2048];
            udpRecvSem.WaitOne();

            while (true) {
                int length = client.ReceiveFrom(buffer, ref point);
                recvCount++;
                this.Dispatcher.Invoke(new Action(delegate {
                    RecvLable.Content = "Recv " + Convert.ToString(recvCount);
                }));
            }
        }

    }
}
